export declare type WebSocketEvents = {
    onOpen?: (event: Event, id: string) => void;
    onMessage?: (event: MessageEvent, id: string) => void;
    onClose?: (event: CloseEvent, id: string) => void;
    onError?: (event: Event, id: string) => void;
};
export declare type ConnectionInfo = {
    url: string;
    protocols?: string | string[];
    events?: WebSocketEvents;
    reconnectTimeout?: number;
    binaryType?: any;
    query?: string;
};
export declare interface IMultiWebSocket extends WebSocketEvents {
    addConnection(id: string, connectionInfo: ConnectionInfo): void;
    removeConnection(id: string): void;
    sendMessage(id: string, message: string | ArrayBufferLike | Blob | ArrayBufferView): void;
    processMessageQueue(id: string): void;
}

// 使用示例
// const multiWebSocket = new MultiWebSocket();

// multiWebSocket.addConnection("socket1", {
//     url: "ws://example.com/socket1",
//     reconnectTimeout: 5000,
//     events: {
//         onOpen: (event, id) => {
//             console.log(`Custom onOpen: Connection "${id}" is open.`);
//         },
//         onMessage: (event, id) => {
//             console.log(`Custom onMessage: Received message from "${id}":`, event.data);
//         },
//         onClose: (event, id) => {
//             console.log(`Custom onClose: Connection "${id}" is closed.`);
//         },
//         onError: (event, id) => {
//             console.log(`Custom onError: Error occurred in connection "${id}".`);
//         },
//     },
// });

// multiWebSocket.addConnection("socket2", {
//     url: "wss://example.com/socket2",
//     reconnectTimeout: 5000,
// });

// multiWebSocket.sendMessage("socket1", "Hello, Socket 1!");
// multiWebSocket.sendMessage("socket2", "Hello, Socket 2!");

// multiWebSocket.removeConnection("socket1");
// multiWebSocket.removeConnection("socket2");

export default class MultiWebSocket<IMultiWebSocket> {
    private connections: Map<string, WebSocket>;
    private messageQueue: Map<string, any[]>;
    private reconnectTimeouts: Map<string, number>;

    constructor() {
        this.connections = new Map();
        this.messageQueue = new Map();
        this.reconnectTimeouts = new Map();
    }

    addConnection(id: string, connectionInfo: ConnectionInfo): void {
        const { url, protocols, events, reconnectTimeout, binaryType, query } = connectionInfo;

        if (this.connections.has(id)) {
            console.warn(`Connection with ID "${id}" already exists.`);
            return;
        }

        const websocket = new WebSocket(url, protocols);
        websocket.binaryType = binaryType || 'arraybuffer'; // 设置接收二进制数据

        websocket.onopen = (event: Event) => {
            this.onOpen(event, id, events?.onOpen);
            this.processMessageQueue(id);
        };

        websocket.onmessage = (event: MessageEvent) =>
            this.onMessage(event, id, events?.onMessage);

        websocket.onclose = (event: CloseEvent) => {
            this.onClose(event, id, events?.onClose);

            if (reconnectTimeout) {
                const timer = setTimeout(() => this.addConnection(id, connectionInfo), reconnectTimeout) as unknown as number;
                this.reconnectTimeouts.set(
                    id,
                    timer
                );
            }
        };

        websocket.onerror = (event: Event) =>
            this.onError(event, id, events?.onError);

        this.connections.set(id, websocket);
        this.messageQueue.set(id, []);
    }

    removeConnection(id: string): void {
        if (!this.connections.has(id)) {
            console.warn(`No connection found with ID "${id}".`);
            return;
        }

        const websocket = this.connections.get(id)!;
        websocket.close();
        this.connections.delete(id);
        this.messageQueue.delete(id);

        if (this.reconnectTimeouts.has(id)) {
            clearTimeout(this.reconnectTimeouts.get(id)!);
            this.reconnectTimeouts.delete(id);
        }
    }

    sendMessage(id: string, message: string | ArrayBuffer): void {
        if (!this.connections.has(id)) {
            console.warn(`No connection found with ID "${id}".`);
            return;
        }

        const websocket = this.connections.get(id)!;

        if (websocket.readyState === WebSocket.OPEN) {
            websocket.send(message);
        } else {
            this.messageQueue.get(id)!.push(message);
        }
    }

    private onOpen(
        event: Event,
        id: string,
        customOnOpen?: (event: Event, id: string) => void
    ): void {
        console.log(`WebSocket connection opened with ID "${id}".`);

        if (customOnOpen) {
            customOnOpen(event, id);
        }
    }

    private onMessage(
        event: MessageEvent,
        id: string,
        customOnMessage?: (event: MessageEvent, id: string) => void
    ): void {
        // console.log(`WebSocket message received from ID "${id}":`, event.data);
        if (customOnMessage) {
            customOnMessage(event, id);
        }
    }

    private onClose(
        event: CloseEvent,
        id: string,
        customOnClose?: (event: CloseEvent, id: string) => void
    ): void {
        console.log(`WebSocket connection closed with ID "${id}".`);

        if (customOnClose) {
            customOnClose(event, id);
        }

        this.connections.delete(id);
    }

    private onError(
        event: Event,
        id: string,
        customOnError?: (event: Event, id: string) => void
    ): void {
        console.error(`WebSocket error occurred with ID "${id}".`);

        if (customOnError) {
            customOnError(event, id);
        }
    }

    private processMessageQueue(id: string): void {
        if (!this.messageQueue.has(id)) {
            return;
        }

        const websocket = this.connections.get(id)!;
        const queue = this.messageQueue.get(id)!;

        while (websocket.readyState === WebSocket.OPEN && queue.length > 0) {
            const message = queue.shift();
            websocket.send(message!);
        }
    }
}
